// 17.2 标准库特殊设施——bitset类型
/**
 * 在4.8节（第135页）中我们介绍了将整型运算对象当作二进制位集合处理的一些内置运算符。标准库还定义了bitset类，使得位运算的使用更为容易，并且能够处理超过最长整型类型大小的位集合。
 * bitset类定义在头文件bitset中。
 */

#include <iterator>
#include <vector>
#include <list>
#include <deque>
#include <forward_list>
#include <string>
#include <array>
#include <stack>
#include <queue>
#include <algorithm>
#include <numeric>
using std::swap;
using std::vector, std::list, std::deque, std::forward_list, std::string, std::array, std::stack, std::queue;
#include "../Chapter07/Sales_data.h"
#include <iostream>
using std::begin, std::cbegin, std::end, std::cend, std::find, std::accumulate, std::equal, std::fill, std::fill_n, std::back_inserter;
using std::cin, std::cout, std::endl;
using std::copy, std::replace, std::replace_copy;
#include <map>
#include <set>
#include <unordered_map>
#include <unordered_set>
#include <utility>
#include <memory>
#include <new>
using namespace std;
#include <functional>
#include "../VisualStudio2012/15/Quote.h"
#include "../VisualStudio2012/12/TextQuery.h"
#include <tuple>
#include <bitset>

int main()
{
    // 17.2.1 定义和初始化bitset
    /*
bitset类是一个类模板，它类似array类，具有固定的大小（参见9.2.4节，第301页）。当我们定义一个bitset时，需要声明它包含多少个二进制位：[插图]
bitset<32> bitvec(1U); // 32位；低位为1，其他位为0
编号从0开始的二进制位被称为低位（low-order），编号到31结束的二进制位被称为高位（high-order）。
    */

    // 用unsigned值初始化bitset
    /*
当我们使用一个整型值来初始化bitset时，此值将被转换为unsigned long long类型并被当作位模式来处理。bitset中的二进制位将是此模式的一个副本。
  如果bitset的大小大于一个unsigned long long中的二进制位数，则剩余的高位被置为0。如果bitset的大小小于一个unsigned long long中的二进制位数，则只使用给定值中的低位，超出bitset大小的高位被丢弃：
// bitvec1比初始值小，初始值中的高位被舍弃
bitset<13> bitvec1(0xbeef); // 0xbeff地的二进制形式为1011 1110 1110 1111，二进制位序列为1 1110 1110 1111
// bitvec2比初始值大，它的高位被置为0
bitset<20> bitvec2(0xbeef); // 0000 1011 1110 1110 1111
// 在64位机器中，long long 0ULL是64个0比特，因此~0ULL是64个1
bitset<128> bitvec3(~0ULL); // 0~63位为1，63~127为为0
    */

    // 从一个string初始化bitset
    /*
我们可以从一个string或一个字符数组指针来初始化bitset。两种情况下，字符都直接表示位模式。与往常一样，当我们使用字符串表示数时，字符串中下标最小的字符对应高位，反之亦然：[插图]
bitset<32> bitvec4("1100"); // 2、3位为1，其余两位为0
string的下标编号习惯与bitset恰好相反：string中下标最大的字符（最右字符）用来初始化bitset中的低位（下标为0的二进制位）。当你用一个string初始化一个bitset时，要记住这个差别。
我们不必使用整个string来作为bitset的初始值，可以只用一个子串作为初始值：[插图]
string str("1111111000000011001101");
bitset<32> bitvec5(str, 5, 4); // 从str[5]开始的4个二进制位，1100
bitset<32> bitvec6(str, string.size() -4); // 使用最后4个字符
    */

    // 17.2.2 bitset操作
    /*
bitset操作（参见表17.3）定义了多种检测或设置一个或多个二进制位的方法。bitset类还支持我们在4.8节（第136页）中介绍过的位运算符。这些运算符用于bitset对象的含义与内置运算符用于unsigned运算对象相同。
b.any()                b中是否存在置位的二进制位
b.all()                b中所有位都置位了吗
b.none()               b中不存在置位的二进制位吗
b.count()              b中置位的位数
b.size()               一个constexpr函数，返回b中的位数
b.test(pos)            若pos位置的位是置位的，则返回true，否则返回false
b.set(pos,v)           将位置pos处的位设置为bool值v。v默认为true。如果未传递实参，则将b中所有位置位
b.set()
b.reset(pos)           将位置pos处的位复位或将b中所有位复位
b.reset()              
b.flip(pos)            改变位置pos处的位的状态或改变b中每一位的状态
b.flip()
b[pos]                 访问b中位置pos处的位：如果b是const的，则当该位置位时，b[pos]返回一个bool值true，否则返回false
b.to_ulong()           返回一个unsinged long或一个unsigned long long值，
b.to_ullong()          其位模式与b相同。如果b中位模式不能放入指定的结果类型，则抛出一个overflow_error异常
b.to_string(zero, one) 返回一个string，表示b中的位模式。zero和one的默认值分别为0和1，用来表示b中的0和1
os << b                将b中二进制位打印为字符1或0，打印到流os中
is >> b                从is读取字符存入b。当下一个字符不是1或0时，或是已经读取b.size()个位时，读取过程停止
count、size、all、any和none等几个操作都不接受参数，返回整个bitset的状态。其他操作——set、reset和flip则改变bitset的状态。
  改变bitset状态的成员函数都是重载的。对每个函数，不接受参数的版本对整个集合执行给定的操作；接受一个位置参数的版本则对指定位执行操作：
bitset<32> bitvec(1U);           // 32位；低位为1，剩余位为0
bool is_set = bitvec.any();      // true，因为有1位置位
bool is_not_set = bitvec.none(); // false，因为有1位置位了
bool all_set = bitvec.all();     // false，因为只有1位置位
size_t onBits = bitvec.count();  // 返回1
size_t sz = bitvec.size();       // 返回32
bitvec.flip();                   // 翻转bitvec中的所有位
bitvec.reset();                  // 将所有位复位
bitvec.set();                    // 将所有位置位
bitvec.flip(0);                  // 翻转位置0处的位
bitvec.set(bitvec.size() -1);    // 置位最后一位
bitvec.set(0,0);                 // 复位位置0处的位
bitvec.reset(i);                 // 复位第i位
bitvec.test(0);                  // 返回false，因为第一位是复位的
bitvec[0] = 0;                   // 将位置0处的位复位
bitvec[31] = bitvec[0];          // 将最后一位设置为与第一位一样
bitvec[0].flip();                // 翻转第一位
~bitvec[0];                      // 翻转第一位
bool b = bitvec[0];              // 将bitvec[0]的值转为bool类型
    */
    
    // 提取bitset的值
    /*
to_ulong和to_ullong操作都返回一个值，保存了与bitset对象相同的位模式。只有当bitset的大小小于等于对应的大小（to_ulong为unsigned long，to_ullong为unsigned long long）时，我们才能使用这两个操作：
unsigned long ulong = bitvec3.to_ulong();
cout << "ulong=" << ulong << endl;
    */
    
    // bitset的IO运算符
    /*
输入运算符从一个输入流读取字符，保存到一个临时的string对象中。直到读取的字符数达到对应bitset的大小时，或是遇到不是1或0的字符时，或是遇到文件尾或输入错误时，读取过程才停止。
  随即用临时string对象来初始化bitset（参见17.2.1节，第642页）。
bitset<16> bits;
cin >> bits; // 从cin读取最多16个0或1
cout << "bits: " << bits << endl; // 打印刚刚读取的内容
    */

    // 使用bitset
    /*
为了说明如何使用bitset，我们重新实现4.8节（第137页）中的评分程序，用bitset代替unsigned long表示30个学生的测验结果——“通过/失败”：
    */
    bool status;
    // 使用位运算符的版本
    unsigned long quizA = 0; // 此值被当作位集合使用
    quizA |= 1UL << 27;      // 指出第27个(从0开始)同学通过了测验
    //cout << quizA << endl;
    status = quizA & (1UL << 27); // 检查第27个同学是否通过了测验
    quizA &= ~(1UL << 27);   // 第27个同学未通过测验
    // 使用标准库类bitset完成等价的工作
    bitset<30> quizB;        // 每个学生分配一位，所有位都被初始化为0
    quizB.set(27);           // 指出第27个学生通过了测验
    status = quizB[27];      // 检查第27个学生是否通过了测验
    quizB.reset(27);        // 第27个学生未通过测验

    return 0;
}